#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <unistd.h>
#include "thread.hpp"
#include "lockGuard.hpp"
#include "log.hpp"

const int g_thread_num = 10;
// 本质是: 生产消费模型
template <class T>
class ThreadPool
{
private:
    ThreadPool(int thread_num = g_thread_num) : num_(thread_num)
    {
        pthread_mutex_init(&lock, nullptr);
        pthread_cond_init(&cond, nullptr);
        for (int i = 1; i <= num_; i++)
        {
            threads_.push_back(new Thread(i, routine, this)); // 传this是线程池对象,因为线程会调用routine，里面需要消费，需要使用到线程池里面的成员
        }
    }
    ThreadPool(const ThreadPool<T> &other) = delete;
    const ThreadPool<T> &operator=(const ThreadPool<T> &other) = delete;

public:
    // 多线程使用单例的过程
    static ThreadPool<T> *getThreadPool(int num = g_thread_num)
    {
        if (nullptr == thread_ptr) 
        {
            lockGuard lockguard(&mutex);
            if (nullptr == thread_ptr)
            {
                thread_ptr = new ThreadPool<T>(num);
            }
        }
        return thread_ptr;
    }
    
    // 1. 线程池里面的线程对象全部保存在threads_中, 调用将threads_所有线程启动
    void run()
    {
        for (auto &iter : threads_)
        {
            iter->start(); // 线程对象里面有start，用来pthread_create
            logMessage(NORMAL, "%s %s", iter->name().c_str(), "启动成功");
        }
    }

    // 线程池本质也是一个生产消费模型（routine用来消费）（pushback用来生产）（队列只有一个，所以生产和消费存在互斥，需要加锁）
    // 这里只用到了一个条件变量, 因为生产时没有判断队列是否为满的情况，也就意味着可以不断往队列里面添加任务
    // 当消费时, 会看队列是否为空,如果为空，挂起此线程，当队列添加任务后会唤起此线程

    // 【消费过程】 消费线程会不断从队列里拿任务,所以需要用while(true)
    static void *routine(void *args)
    {
        ThreadData *td = (ThreadData *)args;
        ThreadPool<T> *tp = (ThreadPool<T> *)td->args_; // 获取线程池对象(需要用到里面的队列和队列锁)
        while (true)
        {
            T task;
            {
                lockGuard lockguard(&(tp->lock));
                while (tp->task_queue_.empty())
                    pthread_cond_wait(&(tp->cond), &(tp->lock));
                
                // 从队列里面拿一个任务
                task = tp-> task_queue_.front(); // 任务队列是共享的-> 将任务从共享，拿到自己的私有空间
                tp->task_queue_.pop();
            }
            task(td->name_); // 启动任务
        }
    }
    // 【生产过程】
    void pushTask(const T &task)
    {
        lockGuard lockguard(&lock);
        task_queue_.push(task);
        pthread_cond_signal(&cond);
    }

    ~ThreadPool()
    {
        for (auto &iter : threads_)
        {
            iter->join();
            delete iter;
        }
        pthread_mutex_destroy(&lock);
        pthread_cond_destroy(&cond);
    }

private:
    int num_; // 线程池里面放几个线程

    std::vector<Thread *> threads_; // 存放线程对象
    std::queue<T> task_queue_;      // 存放任务对象

    static ThreadPool<T> *thread_ptr; // 单例模式
    static pthread_mutex_t mutex;     // 线程池的锁

    pthread_mutex_t lock; // 消息队列的锁
    pthread_cond_t cond;  // 消息队列的条件变量
};

template <typename T>
ThreadPool<T> *ThreadPool<T>::thread_ptr = nullptr;

template <typename T>
pthread_mutex_t ThreadPool<T>::mutex = PTHREAD_MUTEX_INITIALIZER;